#include "Client.h"
#include "World.h"
#include "BlockRender.h"

World::World(Client* cli, u32 seed, int view){
	gePrintDebug(0, "World::World 1\n");
	client = cli;
	ge_CustomVertex* customVert = (ge_CustomVertex*)geMalloc(sizeof(ge_CustomVertex));
	printf("------------SIZE OF mc_Vertex : %d --------------\n", sizeof(mc_Vertex));
	if(client->options.opengl_mode == GL_MODE_FAST){
		customVert->size = sizeof(mc_Vertex_Fast);
		customVert->vertex_offset = 0;
		customVert->vertex_type = GE_SHORT;
		customVert->vertex_count = 4;
		customVert->normal_offset = -1;
		customVert->color_offset = 2 * 4;
		customVert->color_type = GE_UNSIGNED_BYTE;
		customVert->color_count = 4;
		customVert->texture_offset = 2 * 4 + 1 * 4;
		customVert->texture_type = GE_SHORT;
		customVert->texture_count = 4;
	}else
	if(client->options.opengl_mode == GL_MODE_BASIC){
		customVert->size = sizeof(mc_Vertex_Basic);
		customVert->vertex_offset = 0;
		customVert->vertex_type = GE_FLOAT;
		customVert->vertex_count = 3;
		customVert->normal_offset = 4 * 3;
		customVert->normal_type = GE_BYTE;
		customVert->color_offset = 4 * 3 + 1 * 4;
		customVert->color_type = GE_UNSIGNED_BYTE;
		customVert->color_count = 4;
		customVert->texture_offset = 4 * 3 + 1 * 4 + 1 * 4;
		customVert->texture_type = GE_FLOAT;
		customVert->texture_count = 3;
	}else
	if(client->options.opengl_mode == GL_MODE_FULL){
		customVert->size = sizeof(mc_Vertex);
	}
	client->options.vertex_size = customVert->size;

	nChunks = 4 * (view/16) * (view/16);
	chunks = (Chunk*)geMalloc(sizeof(Chunk) * nChunks);
	for(u32 i=0; i<nChunks; i++){
		chunks[i] = Chunk(client);
		chunks[i].setId(i);
		chunks[i].setX(-1);
		chunks[i].setY(-1);
		chunks[i].getRenderer()->customVert = customVert;
		chunks[i].getRenderer()->shader = client->res.shader_lod;
		geRendererLinkLight(chunks[i].getRenderer(), client->res.sun);
		chunks[i].getRenderer()->shader = client->res.shader_base;
		geRendererLinkLight(chunks[i].getRenderer(), client->res.sun);
		geRendererCreateContext(NULL, chunks[i].getRenderer());
	}
}

World::~World(){
}

const Location& World::getSpawn(){
	return spawn;
}

void World::setSpawn(Location loc){	
	spawn.setXYZ(loc.getX(), loc.getY(), loc.getZ());
}

int World::getGameMode(){
	return gameMode;
}

void World::setGameMode(int gm){
	gameMode = gm;
}

std::vector<Entity*>* World::getEntities(){
	return &entities;
}

void World::SpawnEntity(Entity* e){
	if(e){
		entities.push_back(e);
	}
}

Entity* World::SpawnEntity(int id, int type, Location loc){
	for(u32 i=0; i<entities.size(); i++){
		if(entities.at(i)->getId() == id){
			entities.at(i)->setLocation(loc);
			return entities.at(i);
		}
	}
	Entity* e = new Entity(client, id, type);
	e->setLocation(loc);
	entities.push_back(e);
	return e;
}

Entity* World::SpawnObject(int id, int type, Location loc){
	Entity* e = NULL;
	if(type == ENTITY_PAINTING){
		e = new Painting(client, id);
	}else{
		e = new Entity(client, id);
	}
	e->setType(type);
	e->setLiving(false);
	e->setLocation(loc);
	SpawnEntity(e);
	return e;
}

Entity* World::getEntityById(int id){
	for(u32 i=0; i<entities.size(); i++){
		if(entities.at(i)->getId() == id){
			return entities.at(i);
		}
	}
	return NULL;
}

void World::DestroyEntity(int id){
	for(u32 i=0; i<entities.size(); i++){
		if(entities.at(i)->getId() == id){
			entities.erase(entities.begin() + i);
			i--;
		}
	}
}

Chunk* World::getChunks(){
	return chunks;
}

u32 World::getChunksCount(){
	return nChunks;
}

Chunk* World::FindChunk(int x, int y){
	u32 i;
	for(i=0; i<nChunks; i++){
		if((int)((u32)x & 0xFFFFFFF0) == chunks[i].getX() && (int)((u32)y & 0xFFFFFFF0) == chunks[i].getY()){
			return &chunks[i];
		}
	}
	return NULL;
}

Chunk* World::GetUnusedChunk(){
	u32 i;
	for(i=0; i<nChunks; i++){
		if(chunks[i].getX() == -1 && chunks[i].getY() == -1 && !chunks[i].isReserved()){
			return &chunks[i];
		}
	}
	return NULL;
}

Chunk* World::GetMostAwayChunk(int x, int y){
	Chunk* c = &chunks[0];
	u32 i;
	for(i=0; i<nChunks; i++){
		if(!chunks[i].isReserved() && geDistance2D(x, y, chunks[i].getX(), chunks[i].getY()) > geDistance2D(x, y, c->getX(), c->getY())){
			c = &chunks[i];
		}
	}
	return c;
}

Chunk* World::ReserveChunk(){
	Chunk* chunk = GetUnusedChunk();
	if(!chunk){
		chunk = GetMostAwayChunk((int)client->cam->x, (int)client->cam->y);
	}
	if(chunk){
		chunk->setReserved(true);
	}
	return chunk;
}

Block* World::FindBlock(int x, int y, int z){
	return FindBlock(x, y, z, NULL);
}

Block* World::FindBlock(int x, int y, int z, Chunk** chunk){
	if(z < 0){
		return NULL;
	}
	for(u32 i=0; i<nChunks; i++){
		if((int)((u32)x & 0xFFFFFFF0) == chunks[i].getX() && (int)((u32)y & 0xFFFFFFF0) == chunks[i].getY()){
			x = (int)((u32)x & 0x0000000F);
			y = (int)((u32)y & 0x0000000F);
			z = (int)((u32)z & 0x000000FF);
			if(chunk){
				*chunk = &chunks[i];
			}
			return chunks[i].GetBlock(x, y, z);
		}
	}
	if(chunk){
		*chunk = NULL;
	}
	return NULL;
}
